package com.weixin.gong.http;

import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.pool.PoolStats;
import org.apache.http.protocol.HttpContext;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @author weixin.gong
 * @date 17-1-5 下午7:01
 */
public class Executor {

    private static final CloseableHttpClient httpclient;
    private static final PoolingHttpClientConnectionManager cm;
    private static final RequestConfig rc;
    private static final ConnectionKeepAliveStrategy keepAlive;
    private static final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();

    private static final int ConnectionMaxTotal = 500;
    private static final int ConnectionDefaultMaxPerRoute = 50;
    private static final long TIME = 5000;
    private static final int SSL_TIME_OUT = 1000;
    private static final int CONNECTION_REQUEST_TIMEOUT = 1000;
    private static final int CONNECT_TIMEOUT = 10 * 1000;
    private static final int SOCKET_TIMEOUT = 60 * 1000;

    static {
        cm = new PoolingHttpClientConnectionManager();
        cm.setMaxTotal(ConnectionMaxTotal);
        cm.setDefaultMaxPerRoute(ConnectionDefaultMaxPerRoute);
        cm.setDefaultSocketConfig(SocketConfig.custom().setSoTimeout(SSL_TIME_OUT).build());
        cm.setDefaultConnectionConfig(ConnectionConfig.DEFAULT);

        rc = RequestConfig.custom()
                .setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT)
                .setConnectTimeout(CONNECT_TIMEOUT)
                .setSocketTimeout(SOCKET_TIMEOUT)
                .build();

        keepAlive = new DefaultConnectionKeepAliveStrategy() {
            @Override
            public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
                long keepAlive = super.getKeepAliveDuration(response, context);
                return keepAlive > 0 ? keepAlive : TIME;
            }
        };

        httpclient = HttpClients.custom()
                .setConnectionManager(cm)
                .setDefaultRequestConfig(rc)
                .setKeepAliveStrategy(keepAlive)
                .disableAutomaticRetries()
                .build();

        executor.scheduleAtFixedRate(new Runnable() {
            public void run() {
                cm.closeExpiredConnections();
                cm.closeIdleConnections(TIME, TimeUnit.MILLISECONDS);
                try {
                    Field poolField = cm.getClass().getDeclaredField("pool");
                    poolField.setAccessible(true);
                    Object pool = poolField.get(cm);
                    Field routeToPoolField = pool.getClass().getSuperclass().getDeclaredField("routeToPool");
                    routeToPoolField.setAccessible(true);
                    @SuppressWarnings("unchecked")
                    Map<HttpRoute, Object> routeToPool = (Map<HttpRoute, Object>) routeToPoolField.get(pool);
                    for (HttpRoute httpRoute : routeToPool.keySet()) {
                        PoolStats poolStats = cm.getStats(httpRoute);
                        String host = httpRoute.getTargetHost().toHostString();
                        //monitor(host, poolStats);
                    }
                    //monitor("all", cm.getTotalStats());
                } catch (Exception e) {
                    //log.error("HttpUtils timer close connections error", e);
                }

            }
        }, TIME, TIME, TimeUnit.MILLISECONDS);
    }

    public static HttpResponse execute(HttpUriRequest request) throws IOException {
        return httpclient.execute(request);
    }
}
